package com.daffodil.storage.client;

import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;

import com.daffodil.storage.client.sftp.SftpSessionPool;
import com.daffodil.storage.exception.SftpStorageException;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;
import com.jcraft.jsch.ChannelSftp.LsEntrySelector;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;

/**
 * 使用SFTP方式存储文件，本质也是磁盘存储
 * <p>SFTP: Secure File Transfer Protocol 安全文件传输协议</p>
 * @author yweijian
 * @date 2023年11月3日
 * @version 1.0
 * @description
 * Sftp = &#83;&#102;&#116;&#112; exception code = 41300
 */
public class SftpStorageClient {

    private SftpSessionPool pool;

    private Integer connectTimeout;

    public SftpStorageClient(SftpSessionPool pool, Integer connectTimeout) {
        super();
        this.pool = pool;
        this.connectTimeout = connectTimeout;
    }

    /**
     * 连接到 SFTP 服务
     * @return
     * @throws Exception
     */
    protected ChannelSftp connect() throws SftpStorageException {
        Objects.requireNonNull(connectTimeout, "connectTimeout must not be null");
        ChannelSftp channel = null;
        try {
            Session session = pool.borrowObject();
            channel = (ChannelSftp) session.openChannel("sftp");
            if (connectTimeout > 0) {
                channel.connect(connectTimeout);
            } else {
                channel.connect();
            }
        }catch (Exception e) {
            throw new SftpStorageException(41301, "channel connect error: {}", e.getMessage());
        }

        return channel;
    }

    /**
     * 断开与 SFTP 服务的连接
     * @param channel 需要断开连接的通道
     * @throws Exception 如果断开连接失败，则抛出异常
     */
    protected void disconnect(ChannelSftp channel) throws SftpStorageException {
        Objects.requireNonNull(channel, "channel must not be null");
        try {
            channel.disconnect();
            pool.returnObject(channel.getSession());
        }catch (Exception e) {
            throw new SftpStorageException(41302, "channel disconnect error: {}", e.getMessage());
        }
    }

    /**
     * 上传文件对象
     * @param filePath 文件路径
     * @param inputStream 文件对象
     * @throws SftpStorageException
     */
    public void upload(String filePath, InputStream inputStream) throws SftpStorageException {
        Objects.requireNonNull(filePath, "filePath must not be null");
        Objects.requireNonNull(inputStream, "inputStream must not be null");
        ChannelSftp channel = this.connect();
        try (InputStream in = inputStream){
            try {
                channel.cd(filePath.substring(0, filePath.lastIndexOf(StrUtil.SLASH)));
            } catch (SftpException e) {
                channel.mkdir(filePath.substring(0, filePath.lastIndexOf(StrUtil.SLASH)));
            }
            channel.put(in, filePath, 0);
        } catch (SftpException | IOException e) {
            throw new SftpStorageException(41303, "upload storage object error: {}", e.getMessage());
        }finally {
            this.disconnect(channel);
        }
    }

    /**
     * 获取文件对象
     * @param filePath
     * @return
     * @throws Exception
     */
    public InputStream get(String filePath) throws SftpStorageException {
        Objects.requireNonNull(filePath, "filePath must not be null");
        ChannelSftp channel = this.connect();
        try {
            InputStream inputStream = channel.get(filePath);
            byte[] bytes = IoUtil.readBytes(inputStream);
            return IoUtil.toStream(bytes);
        } catch (SftpException e) {
            throw new SftpStorageException(41304, "get storage object error: {}", e.getMessage());
        }finally {
            this.disconnect(channel);
        }
    }

    /**
     * 删除文件对象
     * @param filePath
     * @throws Exception
     */
    public void delete(String filePath) throws SftpStorageException {
        Objects.requireNonNull(filePath, "filePath must not be null");
        ChannelSftp channel = this.connect();
        try {
            channel.rm(filePath);
        } catch (SftpException e) {
            throw new SftpStorageException(41305, "delete storage object error: {}", e.getMessage());
        }finally {
            this.disconnect(channel);
        }
    }

    /**
     * 列出文件对象
     * @param filePath
     */
    public void list(String filePath, LsEntrySelector selector) {
        Objects.requireNonNull(filePath, "filePath must not be null");
        Objects.requireNonNull(selector, "selector must not be null");
        ChannelSftp channel = this.connect();
        try {
            channel.ls(filePath, selector);
        } catch (SftpException e) {
            throw new SftpStorageException(41306, "list storage object error: {}", e.getMessage());
        }finally {
            this.disconnect(channel);
        }
    }

}
